home *** CD-ROM | disk | FTP | other *** search
/ Amiga Games Extra 1996 September / Amiga Games Extra CD-ROM 9-1996.iso / userbox / publicdomain / vim-4.2 / src / quickfix.c < prev    next >
C/C++ Source or Header  |  1996-06-09  |  15KB  |  636 lines

  1. /* vi:set ts=4 sw=4:
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Do ":help uganda"  in Vim to read copying and usage conditions.
  6.  * Do ":help credits" in Vim to see a list of people who contributed.
  7.  */
  8.  
  9. /*
  10.  * quickfix.c: functions for quickfix mode, using a file with error messages
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "option.h"
  17.  
  18. static void qf_free __ARGS((void));
  19. static char_u *qf_types __ARGS((int, int));
  20.  
  21. /*
  22.  * for each error the next struct is allocated and linked in a list
  23.  */
  24. struct qf_line
  25. {
  26.     struct qf_line    *qf_next;    /* pointer to next error in the list */
  27.     struct qf_line    *qf_prev;    /* pointer to previous error in the list */
  28.     linenr_t         qf_lnum;    /* line number where the error occurred */
  29.     int                 qf_fnum;    /* file number for the line */
  30.     int                 qf_col;    /* column where the error occurred */
  31.     int                 qf_nr;        /* error number */
  32.     char_u            *qf_text;    /* description of the error */
  33.     char_u             qf_cleared;/* set to TRUE if line has been deleted */
  34.     char_u             qf_type;    /* type of the error (mostly 'E') */
  35.     char_u             qf_valid;    /* valid error message detected */
  36. };
  37.  
  38. static struct qf_line *qf_start;        /* pointer to the first error */
  39. static struct qf_line *qf_ptr;            /* pointer to the current error */
  40.  
  41. static int    qf_count = 0;        /* number of errors (0 means no error list) */
  42. static int    qf_index;            /* current index in the error list */
  43. static int    qf_nonevalid;        /* set to TRUE if not a single valid entry found */
  44.  
  45. #define MAX_ADDR    7            /* maximum number of % recognized, also adjust
  46.                                     sscanf() below */
  47.  
  48. /*
  49.  * Structure used to hold the info of one part of 'errorformat'
  50.  */
  51. struct eformat
  52. {
  53.     char_u            *fmtstr;        /* pre-formatted part of 'errorformat' */
  54. #ifdef UTS2
  55.     char_u            *(adr[MAX_ADDR]);    /* addresses used */
  56. #else
  57.     void            *(adr[MAX_ADDR]);
  58. #endif
  59.     int                adr_cnt;        /* number of addresses used */
  60.     struct eformat    *next;            /* pointer to next (NULL if last) */
  61. };
  62.  
  63. /*
  64.  * Read the errorfile into memory, line by line, building the error list.
  65.  * Return FAIL for error, OK for success.
  66.  */
  67.     int
  68. qf_init()
  69. {
  70.     char_u             *namebuf;
  71.     char_u            *errmsg;
  72.     int                col;
  73.     int                type;
  74.     int                valid;
  75.     long            lnum;
  76.     int                enr;
  77.     FILE            *fd;
  78.     struct qf_line    *qfp = NULL;
  79.     struct qf_line    *qfprev = NULL;        /* init to make SASC shut up */
  80.     char_u            *efmp;
  81.     struct eformat    *fmt_first = NULL;
  82.     struct eformat    *fmt_last = NULL;
  83.     struct eformat    *fmt_ptr;
  84.     char_u            *efm;
  85.     int                maxlen;
  86.     int                len;
  87.     int                i, j;
  88.     int                retval = FAIL;
  89.  
  90.     if (*p_ef == NUL)
  91.     {
  92.         emsg(e_errorf);
  93.         return FAIL;
  94.     }
  95.  
  96.     namebuf = alloc(CMDBUFFSIZE + 1);
  97.     errmsg = alloc(CMDBUFFSIZE + 1);
  98.     if (namebuf == NULL || errmsg == NULL)
  99.         goto qf_init_end;
  100.  
  101.     if ((fd = fopen((char *)p_ef, "r")) == NULL)
  102.     {
  103.         emsg2(e_openerrf, p_ef);
  104.         goto qf_init_end;
  105.     }
  106.     qf_free();
  107.     qf_index = 0;
  108.  
  109. /*
  110.  * Each part of the format string is copied and modified from p_efm to fmtstr.
  111.  * Only a few % characters are allowed.
  112.  */
  113.     efm = p_efm;
  114.     while (efm[0])
  115.     {
  116.         /*
  117.          * Allocate a new eformat structure and put it at the end of the list
  118.          */
  119.         fmt_ptr = (struct eformat *)alloc((unsigned)sizeof(struct eformat));
  120.         if (fmt_ptr == NULL)
  121.             goto error2;
  122.         if (fmt_first == NULL)        /* first one */
  123.             fmt_first = fmt_ptr;
  124.         else
  125.             fmt_last->next = fmt_ptr;
  126.         fmt_last = fmt_ptr;
  127.         fmt_ptr->next = NULL;
  128.         fmt_ptr->adr_cnt = 0;
  129.  
  130.         /*
  131.          * Isolate one part in the 'errorformat' option
  132.          */
  133.         for (len = 0; efm[len] != NUL && efm[len] != ','; ++len)
  134.             if (efm[len] == '\\' && efm[len + 1] != NUL)
  135.                 ++len;
  136.  
  137.         /*
  138.          * Get some space to modify the format string into.
  139.          * Must be able to do the largest expansion (x3) MAX_ADDR times.
  140.          */
  141.         maxlen = len + MAX_ADDR * 3 + 4;
  142.         if ((fmt_ptr->fmtstr = alloc(maxlen)) == NULL)
  143.             goto error2;
  144.  
  145.         for (i = 0; i < MAX_ADDR; ++i)
  146.             fmt_ptr->adr[i] = NULL;
  147.  
  148.         for (efmp = efm, i = 0; efmp < efm + len; ++efmp, ++i)
  149.         {
  150.             if (efmp[0] != '%')                /* copy normal character */
  151.             {
  152.                 if (efmp[0] == '\\' && efmp + 1 < efm + len)
  153.                     ++efmp;
  154.                 fmt_ptr->fmtstr[i] = efmp[0];
  155.             }
  156.             else
  157.             {
  158.                 fmt_ptr->fmtstr[i++] = '%';
  159.                 switch (efmp[1])
  160.                 {
  161.                 case 'f':        /* filename */
  162.                         fmt_ptr->adr[fmt_ptr->adr_cnt++] = namebuf;
  163.                         /* FALLTHROUGH */
  164.  
  165.                 case 'm':        /* message */
  166.                         if (efmp[1] == 'm')
  167.                             fmt_ptr->adr[fmt_ptr->adr_cnt++] = errmsg;
  168.                         fmt_ptr->fmtstr[i++] = '[';
  169.                         fmt_ptr->fmtstr[i++] = '^';
  170. #ifdef __EMX__
  171.                         /* don't allow spaces in filename. This fixes
  172.                          * the broken sscanf() where an empty message
  173.                          * is accepted as a valid conversion.
  174.                          */
  175.                         if (efmp[1] == 'f')
  176.                             fmt_ptr->fmtstr[i++] = ' ';
  177. #endif
  178.                         if (efmp[2] == '\\')        /* could be "%m\," */
  179.                             j = 3;
  180.                         else
  181.                             j = 2;
  182.                         if (efmp + j < efm + len)
  183.                             fmt_ptr->fmtstr[i++] = efmp[j];
  184.                         else
  185.                         {
  186.                             /*
  187.                              * The %f or %m is the last one in the format,
  188.                              * stop at the CR of NL at the end of the line.
  189.                              */
  190. #ifdef USE_CRNL
  191.                             fmt_ptr->fmtstr[i++] = '\r';
  192. #endif
  193.                             fmt_ptr->fmtstr[i++] = '\n';
  194.                         }
  195.                         fmt_ptr->fmtstr[i] = ']';
  196.                         break;
  197.                 case 'c':        /* column */
  198.                         fmt_ptr->adr[fmt_ptr->adr_cnt++] = &col;
  199.                         fmt_ptr->fmtstr[i] = 'd';
  200.                         break;
  201.                 case 'l':        /* line */
  202.                         fmt_ptr->adr[fmt_ptr->adr_cnt++] = &lnum;
  203.                         fmt_ptr->fmtstr[i++] = 'l';
  204.                         fmt_ptr->fmtstr[i] = 'd';
  205.                         break;
  206.                 case 'n':        /* error number */
  207.                         fmt_ptr->adr[fmt_ptr->adr_cnt++] = &enr;
  208.                         fmt_ptr->fmtstr[i] = 'd';
  209.                         break;
  210.                 case 't':        /* error type */
  211.                         fmt_ptr->adr[fmt_ptr->adr_cnt++] = &type;
  212.                         fmt_ptr->fmtstr[i] = 'c';
  213.                         break;
  214.                 case '%':        /* %% */
  215.                 case '*':        /* %*: no assignment */
  216.                         fmt_ptr->fmtstr[i] = efmp[1];
  217.                         break;
  218.                 default:
  219.                         EMSG("invalid % in format string");
  220.                         goto error2;
  221.                 }
  222.                 if (fmt_ptr->adr_cnt == MAX_ADDR)
  223.                 {
  224.                     EMSG("too many % in format string");
  225.                     goto error2;
  226.                 }
  227.                 ++efmp;
  228.             }
  229.             if (i >= maxlen - 6)
  230.             {
  231.                 EMSG("invalid format string");
  232.                 goto error2;
  233.             }
  234.         }
  235.         fmt_ptr->fmtstr[i] = NUL;
  236.  
  237.         /*
  238.          * Advance to next part
  239.          */
  240.         efm = skip_to_option_part(efm + len);    /* skip comma and spaces */
  241.     }
  242.     if (fmt_first == NULL)        /* nothing found */
  243.     {
  244.         EMSG("'errorformat' contains no pattern");
  245.         goto error2;
  246.     }
  247.  
  248.     /*
  249.      * Read the lines in the error file one by one.
  250.      * Try to recognize one of the error formats in each line.
  251.      */
  252.     while (fgets((char *)IObuff, CMDBUFFSIZE, fd) != NULL && !got_int)
  253.     {
  254.         if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line)))
  255.                                                                       == NULL)
  256.             goto error2;
  257.  
  258.         IObuff[CMDBUFFSIZE] = NUL;    /* for very long lines */
  259.  
  260.         /*
  261.          * Try to match each part of 'errorformat' until we find a complete
  262.          * match or none matches.
  263.          */
  264.         valid = TRUE;
  265.         for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_ptr->next)
  266.         {
  267.             namebuf[0] = NUL;
  268.             errmsg[0] = NUL;
  269.             lnum = 0;
  270.             col = 0;
  271.             enr = -1;
  272.             type = 0;
  273.  
  274.             /*
  275.              * If first char of the format and message don't match, there is
  276.              * no need to try sscanf() on it... Somehow I believe there are
  277.              * very slow implementations of sscanf().
  278.              * -- Paul Slootman
  279.              */
  280.             if (fmt_ptr->fmtstr[0] != '%' && fmt_ptr->fmtstr[0] != IObuff[0])
  281.                 continue;
  282.  
  283.             if (sscanf((char *)IObuff, (char *)fmt_ptr->fmtstr,
  284.                         fmt_ptr->adr[0], fmt_ptr->adr[1], fmt_ptr->adr[2],
  285.                         fmt_ptr->adr[3], fmt_ptr->adr[4], fmt_ptr->adr[5],
  286.                         fmt_ptr->adr[6]) == fmt_ptr->adr_cnt)
  287.                 break;
  288.         }
  289.         if (fmt_ptr == NULL)
  290.         {
  291.             namebuf[0] = NUL;            /* no match found, remove file name */
  292.             lnum = 0;                    /* don't jump to this line */
  293.             valid = FALSE;
  294.             STRCPY(errmsg, IObuff);        /* copy whole line to error message */
  295.             if ((efmp = vim_strrchr(errmsg, '\n')) != NULL)
  296.                 *efmp = NUL;
  297. #ifdef USE_CRNL
  298.             if ((efmp = vim_strrchr(errmsg, '\r')) != NULL)
  299.                 *efmp = NUL;
  300. #endif
  301.         }
  302.  
  303.         if (namebuf[0] == NUL)            /* no file name */
  304.             qfp->qf_fnum = 0;
  305.         else
  306.             qfp->qf_fnum = buflist_add(namebuf);
  307.         if ((qfp->qf_text = strsave(errmsg)) == NULL)
  308.             goto error1;
  309.         qfp->qf_lnum = lnum;
  310.         qfp->qf_col = col;
  311.         qfp->qf_nr = enr;
  312.         qfp->qf_type = type;
  313.         qfp->qf_valid = valid;
  314.  
  315.         if (qf_count == 0)        /* first element in the list */
  316.         {
  317.             qf_start = qfp;
  318.             qfp->qf_prev = qfp;    /* first element points to itself */
  319.         }
  320.         else
  321.         {
  322.             qfp->qf_prev = qfprev;
  323.             qfprev->qf_next = qfp;
  324.         }
  325.         qfp->qf_next = qfp;        /* last element points to itself */
  326.         qfp->qf_cleared = FALSE;
  327.         qfprev = qfp;
  328.         ++qf_count;
  329.         if (qf_index == 0 && qfp->qf_valid)        /* first valid entry */
  330.         {
  331.             qf_index = qf_count;
  332.             qf_ptr = qfp;
  333.         }
  334.         line_breakcheck();
  335.     }
  336.     if (!ferror(fd))
  337.     {
  338.         if (qf_index == 0)        /* no valid entry found */
  339.         {
  340.             qf_ptr = qf_start;
  341.             qf_index = 1;
  342.             qf_nonevalid = TRUE;
  343.         }
  344.         else
  345.             qf_nonevalid = FALSE;
  346.         retval = OK;
  347.         goto qf_init_ok;
  348.     }
  349.     emsg(e_readerrf);
  350. error1:
  351.     vim_free(qfp);
  352. error2:
  353.     qf_free();
  354. qf_init_ok:
  355.     fclose(fd);
  356.     for (fmt_ptr = fmt_first; fmt_ptr != NULL; fmt_ptr = fmt_first)
  357.     {
  358.         fmt_first = fmt_ptr->next;
  359.         vim_free(fmt_ptr->fmtstr);
  360.         vim_free(fmt_ptr);
  361.     }
  362. qf_init_end:
  363.     vim_free(namebuf);
  364.     vim_free(errmsg);
  365.     return retval;
  366. }
  367.  
  368. /*
  369.  * jump to a quickfix line
  370.  * if dir == FORWARD go "errornr" valid entries forward
  371.  * if dir == BACKWARD go "errornr" valid entries backward
  372.  * else if "errornr" is zero, redisplay the same line
  373.  * else go to entry "errornr"
  374.  */
  375.     void
  376. qf_jump(dir, errornr)
  377.     int        dir;
  378.     int        errornr;
  379. {
  380.     struct qf_line    *old_qf_ptr;
  381.     int                old_qf_index;
  382.     static char_u    *e_no_more_errors = (char_u *)"No more errors";
  383.     char_u            *err = e_no_more_errors;
  384.     linenr_t        i;
  385.  
  386.     if (qf_count == 0)
  387.     {
  388.         emsg(e_quickfix);
  389.         return;
  390.     }
  391.  
  392.     old_qf_ptr = qf_ptr;
  393.     old_qf_index = qf_index;
  394.     if (dir == FORWARD)        /* next valid entry */
  395.     {
  396.         while (errornr--)
  397.         {
  398.             old_qf_ptr = qf_ptr;
  399.             old_qf_index = qf_index;
  400.             do
  401.             {
  402.                 if (qf_index == qf_count || qf_ptr->qf_next == NULL)
  403.                 {
  404.                     qf_ptr = old_qf_ptr;
  405.                     qf_index = old_qf_index;
  406.                     if (err != NULL)
  407.                     {
  408.                         emsg(err);
  409.                         return;
  410.                     }
  411.                     errornr = 0;
  412.                     break;
  413.                 }
  414.                 ++qf_index;
  415.                 qf_ptr = qf_ptr->qf_next;
  416.             } while (!qf_nonevalid && !qf_ptr->qf_valid);
  417.             err = NULL;
  418.         }
  419.     }
  420.     else if (dir == BACKWARD)        /* previous valid entry */
  421.     {
  422.         while (errornr--)
  423.         {
  424.             old_qf_ptr = qf_ptr;
  425.             old_qf_index = qf_index;
  426.             do
  427.             {
  428.                 if (qf_index == 1 || qf_ptr->qf_prev == NULL)
  429.                 {
  430.                     qf_ptr = old_qf_ptr;
  431.                     qf_index = old_qf_index;
  432.                     if (err != NULL)
  433.                     {
  434.                         emsg(err);
  435.                         return;
  436.                     }
  437.                     errornr = 0;
  438.                     break;
  439.                 }
  440.                 --qf_index;
  441.                 qf_ptr = qf_ptr->qf_prev;
  442.             } while (!qf_nonevalid && !qf_ptr->qf_valid);
  443.             err = NULL;
  444.         }
  445.     }
  446.     else if (errornr != 0)        /* go to specified number */
  447.     {
  448.         while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
  449.         {
  450.             --qf_index;
  451.             qf_ptr = qf_ptr->qf_prev;
  452.         }
  453.         while (errornr > qf_index && qf_index < qf_count && qf_ptr->qf_next != NULL)
  454.         {
  455.             ++qf_index;
  456.             qf_ptr = qf_ptr->qf_next;
  457.         }
  458.     }
  459.  
  460.     /*
  461.      * If there is a file name, 
  462.      * read the wanted file if needed, and check autowrite etc.
  463.      */
  464.     if (qf_ptr->qf_fnum == 0 || buflist_getfile(qf_ptr->qf_fnum,
  465.                                              (linenr_t)1, GETF_SETMARK) == OK)
  466.     {
  467.         /*
  468.          * Go to line with error, unless qf_lnum is 0.
  469.          */
  470.         i = qf_ptr->qf_lnum;
  471.         if (i > 0)
  472.         {
  473.             if (i > curbuf->b_ml.ml_line_count)
  474.                 i = curbuf->b_ml.ml_line_count;
  475.             curwin->w_cursor.lnum = i;
  476.         }
  477.         if (qf_ptr->qf_col > 0)
  478.         {
  479.             curwin->w_cursor.col = qf_ptr->qf_col - 1;
  480.             adjust_cursor();
  481.         }
  482.         else
  483.             beginline(TRUE);
  484.         cursupdate();
  485.         smsg((char_u *)"(%d of %d)%s%s: %s", qf_index, qf_count, 
  486.                     qf_ptr->qf_cleared ? (char_u *)" (line deleted)" : (char_u *)"",
  487.                     qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
  488.         /*
  489.          * if the message is short, redisplay after redrawing the screen
  490.          */
  491.         if (linetabsize(IObuff) < ((int)p_ch - 1) * Columns + sc_col)
  492.             keep_msg = IObuff;
  493.     }
  494.     else if (qf_ptr->qf_fnum != 0)
  495.     {
  496.         /*
  497.          * Couldn't open file, so put index back where it was.  This could
  498.          * happen if the file was readonly and we changed something - webb
  499.          */
  500.         qf_ptr = old_qf_ptr;
  501.         qf_index = old_qf_index;
  502.       }
  503. }
  504.  
  505. /*
  506.  * list all errors
  507.  */
  508.     void
  509. qf_list(all)
  510.     int all;        /* If not :cl!, only show recognised errors */
  511. {
  512.     BUF                *buf;
  513.     char_u            *fname;
  514.     struct qf_line    *qfp;
  515.     int                i;
  516.  
  517.     if (qf_count == 0)
  518.     {
  519.         emsg(e_quickfix);
  520.         return;
  521.     }
  522.  
  523.     if (qf_nonevalid)
  524.         all = TRUE;
  525.     qfp = qf_start;
  526.     set_highlight('d');        /* Same as for directories */
  527.     for (i = 1; !got_int && i <= qf_count; ++i)
  528.     {
  529.         if (qfp->qf_valid || all)
  530.         {
  531.             msg_outchar('\n');
  532.             start_highlight();
  533.             fname = NULL;
  534.             if (qfp->qf_fnum != 0 &&
  535.                                  (buf = buflist_findnr(qfp->qf_fnum)) != NULL)
  536.                 fname = buf->b_xfilename;
  537.             if (fname == NULL)
  538.                 sprintf((char *)IObuff, "%2d", i);
  539.             else
  540.                 sprintf((char *)IObuff, "%2d %s", i, fname);
  541.             msg_outtrans(IObuff);
  542.             stop_highlight();
  543.             if (qfp->qf_lnum == 0)
  544.                 IObuff[0] = NUL;
  545.             else if (qfp->qf_col == 0)
  546.                 sprintf((char *)IObuff, ":%ld", qfp->qf_lnum);
  547.             else
  548.                 sprintf((char *)IObuff, ":%ld, col %d",
  549.                                                    qfp->qf_lnum, qfp->qf_col);
  550.             sprintf((char *)IObuff + STRLEN(IObuff), "%s: ",
  551.                                         qf_types(qfp->qf_type, qfp->qf_nr));
  552.             msg_outstr(IObuff);
  553.             msg_prt_line(qfp->qf_text);
  554.             flushbuf();                    /* show one line at a time */
  555.         }
  556.         qfp = qfp->qf_next;
  557.         mch_breakcheck();
  558.     }
  559. }
  560.  
  561. /*
  562.  * free the error list
  563.  */
  564.     static void
  565. qf_free()
  566. {
  567.     struct qf_line *qfp;
  568.  
  569.     while (qf_count)
  570.     {
  571.         qfp = qf_start->qf_next;
  572.         vim_free(qf_start->qf_text);
  573.         vim_free(qf_start);
  574.         qf_start = qfp;
  575.         --qf_count;
  576.     }
  577. }
  578.  
  579. /*
  580.  * qf_mark_adjust: adjust marks
  581.  */
  582.    void
  583. qf_mark_adjust(line1, line2, amount, amount_after)
  584.     linenr_t    line1;
  585.     linenr_t    line2;
  586.     long        amount;
  587.     long        amount_after;
  588. {
  589.     register int i;
  590.     struct qf_line *qfp;
  591.  
  592.     if (qf_count)
  593.         for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
  594.             if (qfp->qf_fnum == curbuf->b_fnum)
  595.             {
  596.                 if (qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
  597.                 {
  598.                     if (amount == MAXLNUM)
  599.                         qfp->qf_cleared = TRUE;
  600.                     else
  601.                         qfp->qf_lnum += amount;
  602.                 }
  603.                 if (amount_after && qfp->qf_lnum > line2)
  604.                     qfp->qf_lnum += amount_after;
  605.             }
  606. }
  607.  
  608. /*
  609.  * Make a nice message out of the error character and the error number:
  610.  *    char    number        message
  611.  *  e or E    0            "   error"
  612.  *  w or W    0            " warning"
  613.  *  other     0            ""
  614.  *  w or W    n            " warning n"
  615.  *  other     n            "   error n"
  616.  */
  617.     static char_u *
  618. qf_types(c, nr)
  619.     int c, nr;
  620. {
  621.     static char_u    buf[20];
  622.     char_u        *p1;
  623.  
  624.     p1 = (char_u *)"   error";
  625.     if (c == 'W' || c == 'w')
  626.         p1 =  (char_u *)" warning";
  627.     else if (nr <= 0 && c != 'E' && c != 'e')
  628.         p1 = (char_u *)"";
  629.  
  630.     if (nr <= 0)
  631.         return p1;
  632.  
  633.     sprintf((char *)buf, "%s %3d", p1, nr);
  634.     return buf;
  635. }
  636.